package com.cw.elasticsearch.service.impl;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import org.apache.lucene.search.TotalHits;
import org.elasticsearch.action.DocWriteResponse;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.admin.indices.open.OpenIndexRequest;
import org.elasticsearch.action.admin.indices.open.OpenIndexResponse;
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsRequest;
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.core.CountRequest;
import org.elasticsearch.client.core.CountResponse;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.client.indices.GetMappingsRequest;
import org.elasticsearch.client.indices.GetMappingsResponse;
import org.elasticsearch.cluster.metadata.MappingMetadata;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.elasticsearch.xcontent.XContentBuilder;
import org.elasticsearch.xcontent.XContentFactory;
import org.elasticsearch.xcontent.XContentType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

import com.alibaba.fastjson2.JSON;
import com.cw.elasticsearch.service.ElasticsearchService;

/**
 * elasticsearch服务
 *
 * @author chenw
 * @date 2020/12/20
 */
public class ElasticsearchServiceImpl implements ElasticsearchService {
    private static final Logger logger = LoggerFactory.getLogger(ElasticsearchServiceImpl.class);
    @Autowired
    @Qualifier("restHighLevelClient")
    private RestHighLevelClient client;

    private static String numberOfShards = "index.number_of_shards";
    private static String numberOfReplicas = "index.number_of_replicas";

    @Override
    public Boolean createIndex(String indexName) {
        Boolean success = false;
        CreateIndexRequest request = new CreateIndexRequest(indexName);
        request.settings(Settings.builder()
            .put(numberOfShards, "5")
            .put(numberOfReplicas, "0")
            .build());
        Map<String, Object> message = new HashMap<>();
        message.put("type", "text");
        Map<String, Object> name = new HashMap<>();
        name.put("type", "keyword");
        Map<String, Object> title = new HashMap<>();
        title.put("type", "text");
        Map<String, Object> properties = new HashMap<>();
        properties.put("message", message);
        properties.put("name", name);
        properties.put("title", title);
        Map<String, Object> mapping = new HashMap<>();
        mapping.put("properties", properties);
        request.mapping(mapping);

        CreateIndexResponse createIndexResponse;
        try {
            createIndexResponse = client.indices()
                .create(request, RequestOptions.DEFAULT);
            success = true;
            logger.info(createIndexResponse.toString());
        } catch (IOException e) {
            logger.error("创建索引失败:{}", e.getMessage());
        }
        return success;
    }

    @Override
    public Boolean createIndexTwo(String indexName) {
        boolean success = false;
        CreateIndexRequest request = new CreateIndexRequest(indexName);
        request.settings(Settings.builder()
            .put(numberOfShards, 5)
            .put(numberOfReplicas, 0));
        try {
            XContentBuilder builder = XContentFactory.jsonBuilder();
            builder.startObject();
            {
                builder.startObject("properties");
                {
                    builder.startObject("message");
                    {
                        builder.field("type", "text");
                    }
                    builder.endObject();
                    builder.startObject("title");
                    {
                        builder.field("type", "text");
                        builder.field("analyzer", "ik_max_word");
                    }
                    builder.endObject();
                }
                builder.endObject();
            }
            builder.endObject();
            request.mapping(builder);
            CreateIndexResponse createIndexResponse = client.indices()
                .create(request, RequestOptions.DEFAULT);
            logger.info(createIndexResponse.index());
        } catch (Exception e) {
            logger.error("创建索引失败:{}", e.getMessage());
        }
        return success;
    }

    @Override
    public Boolean deleteIndex(String indexName) {
        DeleteIndexRequest request = new DeleteIndexRequest(indexName);
        AcknowledgedResponse deleteIndexResponse = null;
        try {
            deleteIndexResponse = client.indices()
                .delete(request, RequestOptions.DEFAULT);
            return deleteIndexResponse.isAcknowledged();
        } catch (IOException e) {
            logger.error("删除索引失败:{}", e.getMessage());
        }
        return false;
    }

    @Override
    public Boolean existsIndex(String indexName) {
        GetIndexRequest request = new GetIndexRequest(indexName);
        Boolean exists = false;
        try {
            exists = client.indices()
                .exists(request, RequestOptions.DEFAULT);
            logger.info(exists.toString());
        } catch (IOException e) {
            logger.error("检查索引是否存在失败:{}", e.getMessage());
        }
        return exists;
    }

    @Override
    public Boolean openIndex(String indexName) {

        Boolean success = false;
        OpenIndexRequest request = new OpenIndexRequest(indexName);
        OpenIndexResponse openIndexResponse = null;
        try {
            openIndexResponse = client.indices()
                .open(request, RequestOptions.DEFAULT);
            logger.info(openIndexResponse.toString());
            success = true;
        } catch (IOException e) {
            logger.error("打开索引失败:{}", e.getMessage());
        }
        return success;
    }

    @Override
    public Map<String, Object> getIndexMapping(String indexName) {
        GetMappingsRequest request = new GetMappingsRequest();
        request.indices(indexName);
        GetMappingsResponse getMappingResponse = null;
        try {
            getMappingResponse = client.indices()
                .getMapping(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            logger.error("获取索引的mapping失败:{}", e.getMessage());
        }
        Map<String, MappingMetadata> allMappings = getMappingResponse.mappings();
        MappingMetadata indexMapping = allMappings.get(indexName);
        Map<String, Object> mapping = indexMapping.sourceAsMap();
        for (String s : mapping.keySet()) {
            logger.info("key : " + s + " value : " + mapping.get(s));
        }
        return mapping;
    }

    @Override
    public String getIndexSettings(String indexName) {
        GetSettingsRequest request = new GetSettingsRequest().indices(indexName);

        GetSettingsResponse getSettingsResponse = null;
        try {
            getSettingsResponse = client.indices()
                .getSettings(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            logger.error("获取索引设置失败：{}", e.getMessage());
        }
        String numberOfShardsString = getSettingsResponse.getSetting(indexName, numberOfShards);
        String numberOfReplicasString = getSettingsResponse.getSetting(indexName, numberOfReplicas);
        Map<String, Object> map = new HashMap<>();
        map.put(numberOfShards, numberOfShardsString);
        map.put(numberOfReplicas, numberOfReplicasString);
        return JSON.toJSONString(map);
    }

    @Override
    public Boolean insertDocument(String indexName, String id, String json) {
        boolean success = false;
        IndexRequest indexRequest = new IndexRequest(indexName).id(id)
                .source(json, XContentType.JSON);
        IndexResponse indexResponse;
        try {
            indexResponse = client.index(indexRequest, RequestOptions.DEFAULT);
            logger.info("indexResponse:{}", indexResponse.toString());
            success = true;
        } catch (IOException e) {
            logger.error("新增文档失败：{}", e.getMessage());
        }
        return success;
    }

    @Override
    public GetResponse getDocumentById(String indexName, String id, String[] includes, String[] excludes) {
        GetRequest getRequest = new GetRequest(indexName, id);
        /*
         * 查询指定字段
         */
        //String[] includes = new String[]{"name", "create_time","title"};
        //String[] excludes = Strings.EMPTY_ARRAY;
        FetchSourceContext fetchSourceContext = new FetchSourceContext(true, includes, excludes);
        getRequest.fetchSourceContext(fetchSourceContext);
        /*
          排除指定字段
         */
        // String[] includes = Strings.EMPTY_ARRAY;
        // String[] excludes = new String[]{"number"};
        // FetchSourceContext fetchSourceContext =
        //     new FetchSourceContext(true, includes, excludes);
        // getRequest.fetchSourceContext(fetchSourceContext);

        GetResponse getResponse = null;
        try {
            getResponse = client.get(getRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }

        String index = getResponse.getIndex();
        if (getResponse.isExists()) {
            long version = getResponse.getVersion();
            logger.info("查询版本号，version:{}", version);
            String sourceAsString = getResponse.getSourceAsString();
            logger.info("查询结果string类型，sourceAsString:{}", sourceAsString);
            Map<String, Object> sourceAsMap = getResponse.getSourceAsMap();
            logger.info("查询结果map类型，sourceAsMap:{}", sourceAsMap);
            byte[] sourceAsBytes = getResponse.getSourceAsBytes();
            logger.info("倒排索引，sourceAsBytes:{}", sourceAsBytes);
        } else {
            logger.info("暂无数据");
        }
        logger.info("index:{}", index);
        logger.info("id:{}", getResponse.getId());
        return getResponse;
    }

    @Override
    public Boolean existsDocument(String indexName, String id) {
        GetRequest getRequest = new GetRequest(indexName, id);
        //禁用获取_source
        getRequest.fetchSourceContext(new FetchSourceContext(false));
        //禁用获取存储的字段。
        getRequest.storedFields("_none_");
        boolean exists = false;
        try {
            exists = client.exists(getRequest, RequestOptions.DEFAULT);
            logger.info("exists:{}", exists);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return exists;
    }

    @Override
    public UpdateResponse updateDocument(String indexName, String id, String json) {
        UpdateRequest request = new UpdateRequest(indexName, id).doc(json, XContentType.JSON);
        UpdateResponse updateResponse = null;
        try {
            updateResponse = client.update(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
        String index = updateResponse.getIndex();
        logger.info("index:{}", index);
        logger.info("id:{}", updateResponse.getId());
        long version = updateResponse.getVersion();
        logger.info("version:{}", version);

        if (updateResponse.getResult() == DocWriteResponse.Result.CREATED) {
            logger.info("处理首次创建文档的情况");
        } else if (updateResponse.getResult() == DocWriteResponse.Result.UPDATED) {
            logger.info("处理文档更新的情况");
        } else if (updateResponse.getResult() == DocWriteResponse.Result.DELETED) {
            logger.info("处理文件被删除的情况");
        } else if (updateResponse.getResult() == DocWriteResponse.Result.NOOP) {
            logger.info("处理文档不受更新影响的情况，即未对文档执行任何操作（空转）");
        }
        return updateResponse;
    }

    @Override
    public DeleteResponse deleteDocument(String indexName, String id) {
        DeleteRequest request = new DeleteRequest(indexName, id);
        DeleteResponse deleteResponse = null;
        try {
            deleteResponse = client.delete(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
        logger.info("deleteResponse:{}", deleteResponse);
        return deleteResponse;
    }

    /**
     * 搜索文档
     *
     * @param indexName
     * @param name
     * @param text
     * @return
     */
    @Override
    public SearchResponse searchDocument(String indexName, String name, String text) {

        SearchRequest searchRequest = new SearchRequest(indexName);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.matchQuery(name, text));
        searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
        searchRequest.source(searchSourceBuilder);
        SearchResponse searchResponse = null;
        try {
            searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
        float maxScore = searchResponse.getHits()
            .getMaxScore();
        TotalHits totalHits = searchResponse.getHits()
            .getTotalHits();
        SearchHit[] hits = searchResponse.getHits()
            .getHits();

        for (SearchHit hit : hits) {
            logger.info("id:{}", hit.getId());
            logger.info("索引名:{}", hit.getIndex());
            logger.info("分数:{}", hit.getScore());
            logger.info("string:{}", hit.getSourceAsString());
            logger.info("map:{}", hit.getSourceAsMap());
        }

        logger.info("totalHits value:{}", totalHits.value);
        logger.info("totalHits relation:{}", totalHits.relation);
        logger.info("maxScore:{}", maxScore);
        logger.info("searchResponse:{}", searchResponse);
        return searchResponse;
    }

    /**
     * 搜索文档
     *
     * @param indexName
     * @param name
     * @param text
     * @return
     */
    @Override
    public SearchResponse searchDocumentTwo(String indexName, String name, String text) {
        /*
         指定查询book索引，不指定查询所有索引
         */
        SearchRequest searchRequest = new SearchRequest(indexName);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(QueryBuilders.matchQuery(name, text));
        sourceBuilder.from(0);
        sourceBuilder.size(1);
        sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
        searchRequest.source(sourceBuilder);

        SearchResponse searchResponse = null;
        try {
            searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
        SearchHit[] hits;
        hits = searchResponse.getHits()
            .getHits();

        for (SearchHit hit : hits) {
            logger.info("id:{}", hit.getId());
            logger.info("索引名:{}", hit.getIndex());
            logger.info("分数:{}", hit.getScore());
            logger.info("string:{}", hit.getSourceAsString());
            logger.info("map:{}", hit.getSourceAsMap());
        }
        return searchResponse;
    }

    /**
     * 统计搜索文档
     *
     * @param name
     * @param text
     * @return
     */
    @Override
    public CountResponse countSearchDocument(String name, String text) {
        CountRequest countRequest = new CountRequest();
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.matchAllQuery());
        countRequest.source(searchSourceBuilder);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(QueryBuilders.matchQuery(name, text));
        countRequest.source(sourceBuilder);
        CountResponse countResponse = null;
        try {
            countResponse = client.count(countRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
        long count = countResponse.getCount();
        logger.info("count:{}", count);
        return countResponse;
    }
}
